#include <stdio.h>   /* Standard input/output definitions */
#include <string.h>  /* String function definitions */
#include <unistd.h>  /* UNIX standard function definitions */
#include <fcntl.h>   /* File control definitions */
#include <errno.h>   /* Error number definitions */
#include <syslog.h>

#include "SDL.h"
#include "PDL.h" 

#include <pthread.h>
#include <dlfcn.h>
#include <poll.h>
#include <hal/hal_touchpanel.h>

//Declare global variables
static hal_device_handle_t d = NULL;
const char *hal_dl_name = "/usr/lib/libhal.so";
void* hal_dl_handle = NULL;
hal_error_t (*hal_device_open_func)(hal_device_type_t, hal_device_id_t, hal_device_handle_t*);
hal_error_t (*hal_device_close_func)(hal_device_handle_t);
hal_error_t (*hal_device_get_event_source_func)(hal_device_handle_t, int*);
hal_error_t (*hal_device_get_event_func)(hal_device_handle_t, hal_event_handle_t*);
hal_error_t (*hal_device_release_event_func)(hal_device_handle_t, hal_event_handle_t);

hal_error_t (*hal_touchpanel_event_get_type_func)(hal_event_handle_t, hal_touchpanel_event_type_t *);
hal_error_t (*hal_touchpanel_event_get_touches_func) (hal_event_handle_t, hal_touchpanel_event_item_t**, int *);


//Declare macros
#define DCLOSE() do { hal_device_close_func(d); d = NULL; } while (0)

static void report_result(const char* result)
{
	const char* params[1];
	PDL_Err mjErr;
	
	params[0] = result;
	mjErr = PDL_CallJS("touchpanelEventLoopCallBack", params, 1);
  if ( mjErr != PDL_NOERROR )                 
  {    	           
     syslog(LOG_INFO, "SamDebug: %s: PDL_CallJS - error = %s\n", __func__, PDL_GetError());          
  }
}

static void touch_item_print(hal_touchpanel_event_item_t* p_item)
{

  const char* p_state;
  char data[128];

  if (HAL_TOUCHPANEL_STATE_DOWN == p_item->state) {
    p_state = "Down";
  }
  else if (HAL_TOUCHPANEL_STATE_UP == p_item->state) {
    p_state = "Up";
  }
  else {
    p_state = "";
  }
  snprintf(data, 128, "[Touch] finger %d %s(%d), timestamp %08lld, (x,y) = (%d, %d) \n",
      p_item->finger,
      p_state,
      p_item->state,
      p_item->timestamp,
      p_item->x,
      p_item->y);

	report_result((const char *)&data[0]);
}

static void poll_for_event(struct pollfd events)
{
  int fds_ready;
  hal_error_t error = HAL_ERROR_SUCCESS;

  syslog(LOG_INFO, "SamDebug: +++%s\n", __func__);

  events.events = POLLIN;
  fds_ready = poll(&events, 1, 25000);
  syslog(LOG_INFO, "SamDebug: %s: fds_ready = %d\n", __func__, fds_ready);
  if (fds_ready != 1)
  	return;

  if ((events.revents & POLLIN) != POLLIN)
  	return;
	
  for (int i =0;i < 50 ;i++) {
  	
  	syslog(LOG_INFO, "SamDebug: %s: start getting event..., %dth time\n", __func__, i);
  	
    hal_event_handle_t event_handle = NULL;

    if (d == NULL)
    	return;

    error = hal_device_get_event_func(d, &event_handle);
    syslog(LOG_INFO, "SamDebug: %s: hal_device_get_event_func - error = %d, event_handle = 0x%x\n", __func__, error, event_handle);
    if (error != HAL_ERROR_SUCCESS)
    	return;

    // TODO: add generic event type query to identify the class of event
    if (NULL != event_handle) {
      /*
       * Handle event sub-class
       */

      hal_touchpanel_event_item_t* touches;
      int count = 0;

      error = hal_touchpanel_event_get_touches_func(event_handle, &touches, &count);

			syslog(LOG_INFO, "SamDebug: %s: hal_touchpanel_event_get_touches_func - error = %d, count = %d\n", __func__, error, count);
      if (error != HAL_ERROR_SUCCESS || count == 0) 
      	return;
      	
      for (int j=0; j<count; j++) {
          touch_item_print(touches++);
      }

      hal_device_release_event_func(d, event_handle);

    }
    else
    {
      return;
    }
  }

	syslog(LOG_INFO, "SamDebug: ---%s\n", __func__);
}

void *simple_event_loop(void *ptr){
	struct pollfd events;
	hal_error_t error = HAL_ERROR_SUCCESS;

	syslog(LOG_INFO, "SamDebug: +++%s\n", __func__);
	
	error = hal_device_open_func(HAL_DEVICE_TOUCHPANEL, "Main", &d);
	syslog(LOG_INFO, "SamDebug: %s: hal_device_open_func - error = %d\n", __func__, error);
	if (error != HAL_ERROR_SUCCESS || d == NULL) {
     report_result("QUIT: Failed to open HAL_DEVICE_TOUCHPANEL");
     return ((void *)0);
	}
	
	error = hal_device_get_event_source_func(d, &events.fd);
	syslog(LOG_INFO, "SamDebug: %s: hal_device_get_event_source_func - error = %d\n", __func__, error);
	if (error != HAL_ERROR_SUCCESS || events.fd == -1) {
     report_result("QUIT: Failed to get event source");
     return ((void *)0);
	} 
	
	int i = 0;
	
	while (i < 50) {
		syslog(LOG_INFO, "SamDebug: %s: start polling..., %dth time\n", __func__, i);
		poll_for_event(events);
		i++;
	}
	
	DCLOSE();
	
	report_result("QUIT: Completed");
	
	return ((void *)0);
}

PDL_bool
start_simple_event_loop(PDL_JSParameters *params) {
	char reply[64];
	pthread_t 	thread_loop;
	int 		thread_id = 0;
	
	#define CHECK_FUN(HALFUN) else if (HALFUN##_func == NULL) snprintf(reply, 64, "Can't load function: %s!", #HALFUN);
	
	if (hal_dl_handle == NULL)
		snprintf(reply, 64, "Can't load hal libary - libhal.so");
	CHECK_FUN(hal_device_open)
	CHECK_FUN(hal_device_close)
	CHECK_FUN(hal_device_get_event_source)
	CHECK_FUN(hal_device_get_event)
	CHECK_FUN(hal_device_release_event)
	CHECK_FUN(hal_touchpanel_event_get_type)
	CHECK_FUN(hal_touchpanel_event_get_touches)
	else {
		thread_id = pthread_create(&thread_loop, NULL, simple_event_loop, NULL);
		if (thread_id != 0)
			snprintf(reply, 64, "Start simple_event_loop, error: %s!", strerror(thread_id));
		else
			snprintf(reply, 64, "Start simple_event_loop OK!");
	}
	
	PDL_JSReply(params, reply);
	return PDL_TRUE;

}


PDL_bool 
quit(PDL_JSParameters *params)
{
    SDL_QuitEvent quitEvent;
    
    quitEvent.type = SDL_QUIT;    
    SDL_PushEvent ((SDL_Event*)&quitEvent);
    
    return PDL_TRUE;
}


int main(int argc, char** argv)
{
  openlog("com.hp.testhaltouchpanel", 0, LOG_USER);
    
  if ( SDL_Init(SDL_INIT_VIDEO) != 0 )
  {
    printf("Could not init SDL: %s\n", SDL_GetError());
    exit(1);
  }
  
  PDL_Init(0);
  
  // register the js callback
  PDL_RegisterJSHandler("start_simple_event_loop", start_simple_event_loop);
  PDL_RegisterJSHandler("quit", quit);
  PDL_JSRegistrationComplete();
  
  syslog(LOG_INFO, "SamDebug: before loading libary\n");
  // Load hal libary
  hal_dl_handle = dlopen(hal_dl_name, RTLD_NOW);
	if (hal_dl_handle) {
			hal_device_open_func =
				(hal_error_t (*)(hal_device_type_t, hal_device_id_t, hal_device_handle_t*))
					dlsym(hal_dl_handle, "hal_device_open");
					
			hal_device_close_func =
				(hal_error_t (*)(hal_device_handle_t))
					dlsym(hal_dl_handle, "hal_device_close");\

			hal_device_get_event_source_func =
				(hal_error_t (*)(hal_device_handle_t, int*))
					dlsym(hal_dl_handle, "hal_device_get_event_source");

			hal_device_get_event_func = 
				(hal_error_t (*)(hal_device_handle_t, hal_event_handle_t*))
					dlsym(hal_dl_handle, "hal_device_get_event");

			hal_device_release_event_func =
				(hal_error_t (*)(hal_device_handle_t, hal_event_handle_t))
					dlsym(hal_dl_handle, "hal_device_release_event");

			hal_touchpanel_event_get_type_func = 
				(hal_error_t (*)(hal_event_handle_t, hal_touchpanel_event_type_t *))
					dlsym(hal_dl_handle, "hal_touchpanel_event_get_type");
					
			hal_touchpanel_event_get_touches_func = 
				(hal_error_t (*)(hal_event_handle_t, hal_touchpanel_event_item_t**,int *))
					dlsym(hal_dl_handle, "hal_touchpanel_event_get_touches");			
  }
  syslog(LOG_INFO, "SamDebug: after loading libary\n");
  // Event descriptor
  SDL_Event event;
  
  //Loop till SDL_QUIT comes
  do
  {
    SDL_PollEvent(&event);
    
  }while (event.type != SDL_QUIT);

  // Cleanup
  PDL_Quit();
  SDL_Quit();
  closelog();
}

